package com.kuangjia.boss.commonSafety.zipEncryption.logTask;

import lombok.extern.slf4j.Slf4j;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * Log日志Zip加密需输入密码看日志 -- 用于安全隐患泄露处理
 * </p>
 *
 * @author JiangYiji
 * @since 2022-11-11

 * 更新日志：
 * 1. 2024-08-24 -- 新增删除一个月之前加密后的 '.encryption.zip',新增 deleteOldEncryptedZipFiles();方法  -- JiangYiji
 * 2.
 * 3.
 *
 */
@Slf4j
@Component
public class LogEncryptor {

    @Value("${logEncrypt.enable}")
    private boolean enable;

    private static String LOG_DIR = "";

    private static final String PASSWORD = "Exhibition@logs";

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");

    private static String getLogsDirectory() {
        String currentDir = System.getProperty("user.dir");
        return currentDir + File.separator + "logs";
    }

    @Scheduled(cron = "0 10 0 * * ?")
    public void encryptLogFiles() {
        if (enable) {
            LOG_DIR = getLogsDirectory();
            String dateStr = new SimpleDateFormat("yyyy-MM-dd").format(new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000));
            log.info("【日志加密-定时任务】昨天的日期：" + dateStr);

            File logDir = new File(LOG_DIR);
            File[] subDirs = logDir.listFiles(File::isDirectory);

            if (subDirs != null) {
                for (File subDir : subDirs) {
                    File[] filesToZip = subDir.listFiles((dir, name) -> name.contains(dateStr) && name.endsWith(".zip"));
                    if (filesToZip != null && filesToZip.length > 0) {
                        for (File file : filesToZip) {
                            log.info("【日志加密-定时任务】找到符合条件的日志文件：" + file.getName());
                            try {
                                encryptAndZipFile(file, PASSWORD);
                            } catch (ZipException e) {
                                e.printStackTrace();
                            }
                        }
                    } else {
                        log.info("【日志加密-定时任务】在目录 " + subDir.getName() + " 中未找到符合条件的日志文件");
                    }
                }
            }
            // 调用删除超过一个月的加密zip文件
            deleteOldEncryptedZipFiles();
        }
    }


    private void encryptAndZipFile(File fileToEncrypt, String password) throws ZipException {

        // 构造加密后的ZIP文件名，并指定保存路径为原始文件路径
        String encryptedZipFileName = fileToEncrypt.getName().replace(".zip", ".encryption.zip");
        File encryptedZipFile = new File(fileToEncrypt.getParent(), encryptedZipFileName);
        log.info("【日志加密-定时任务】加密后的文件名：" + encryptedZipFile.getAbsolutePath());

        // 解压原始zip文件
        ZipFile originalZipFile = new ZipFile(fileToEncrypt);
        String tempDirPath = fileToEncrypt.getParent() + File.separator + "temp";
        File tempDir = new File(tempDirPath);
        originalZipFile.extractAll(tempDirPath);

        // 创建一个新的ZipFile对象，指定加密后的ZIP文件路径
        ZipFile zipFile = new ZipFile(encryptedZipFile);

        // 设置压缩和加密参数
        ZipParameters parameters = new ZipParameters();
        parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
        parameters.setEncryptFiles(true);
        parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD);
        parameters.setPassword(password);

        // 获取解压后的所有文件
        File[] filesToAdd = tempDir.listFiles((dir, name) -> name.endsWith(".txt"));
        List<File> fileList = new ArrayList<>();
        if (filesToAdd != null) {
            for (File file : filesToAdd) {
                fileList.add(file);
            }
        }

        // 创建临时文件列表，加密的文件添加到列表
        ArrayList<File> fileList1 = new ArrayList<>();
        fileList1.add(fileToEncrypt);

        // 将文件添加到新的加密zip文件中
        zipFile.addFiles(fileList1, parameters);
        log.info("【日志加密-定时任务】日志文件已加密并压缩到：" + encryptedZipFile.getAbsolutePath());

        // 删除解压后临时文件
        deleteDirectory(tempDir);

        // 删除原始zip文件
        if (fileToEncrypt.delete()) {
            log.info("【日志加密-定时任务】原始的zip文件已删除：" + fileToEncrypt.getAbsolutePath());
        } else {
            log.error("【日志加密-定时任务】删除原始的zip文件失败：" + fileToEncrypt.getAbsolutePath());
        }
    }

    // 辅助方法：删除文件
    private void deleteDirectory(File directory) {
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectory(file);
                } else {
                    file.delete();
                }
            }
        }
        directory.delete();
    }

    // 删除超过一个月的加密ZIP文件
    private void deleteOldEncryptedZipFiles() {
        File logDir = new File(LOG_DIR);
        File[] subDirs = logDir.listFiles(File::isDirectory);

        if (subDirs != null) {
            for (File subDir : subDirs) {
                File[] encryptedZipFiles = subDir.listFiles((dir, name) -> name.endsWith(".encryption.zip"));

                if (encryptedZipFiles != null) {
                    for (File encryptedZipFile : encryptedZipFiles) {
                        if (isOlderThanOneMonthByName(encryptedZipFile.getName())) {
                            if (encryptedZipFile.delete()) {
                                log.info("【日志加密-定时任务】删除超过一个月的加密ZIP文件：" + encryptedZipFile.getAbsolutePath());
                            } else {
                                log.error("【日志加密-定时任务】删除超过一个月的加密ZIP文件失败：" + encryptedZipFile.getAbsolutePath());
                            }
                        }

                    }
                }
            }
        }
    }

    private boolean isOlderThanOneMonthByName(String fileName) {
        try {
            // 找到第二个下划线的位置
            int firstUnderscoreIndex = fileName.indexOf('_');
            int secondUnderscoreIndex = fileName.indexOf('_', firstUnderscoreIndex + 1);

            // 从第二个下划线开始提取日期部分
            String datePart = fileName.substring(secondUnderscoreIndex + 1, secondUnderscoreIndex + 11);
            Date fileDate = DATE_FORMAT.parse(datePart);

            Calendar cal = Calendar.getInstance();
            cal.add(Calendar.MONTH, -1);
            Date oneMonthAgo = cal.getTime();

            return fileDate.before(oneMonthAgo);
        } catch (ParseException e) {
            log.error("无法解析文件名中的日期：" + fileName, e);
            return false;
        }
    }

}